﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpMoveUtils.h"
#include "ElementTransformationHandlerManager.h"
#include "ElementTransformationHandler.h"
#include "ElementJoinUtils.h"
#include "IElementTransformationComponent.h"
#include "IElementMoveContext.h"
#include "ILine3d.h"
#include "IArc3d.h"
#include "ICurve3dCurve3dIntersection.h"
#include "AlgorithmIntersect.h"
#include "IElement.h"
#include "IDocument.h"
#include "IElementPositionPoints.h"
#include "IModifyJoinRelationship.h"
#include "IElementBasicInformation.h"
#include "IRegenerator.h"
#include "IElementJoin.h"
#include "IJoinRelationshipBehavior.h"
#include "IGeometryRelationshipComponent.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    struct MoveElementJoinData
    {
        MoveElementJoinData(ElementId movedId, JoinPosition movedPos, ElementId joinedId, JoinPosition joinedPos)
            : movedElementId(movedId)
            , movedPosition(movedPos)
            , joinedElementId(joinedId)
            , joinedPosition(joinedPos)
        {}

        ElementId movedElementId;           ///<  移动对象id
        JoinPosition   movedPosition;       ///<  移动对象连接位置
        ElementId joinedElementId;          ///<  和移动对象连接的对象id
        JoinPosition   joinedPosition;      ///<  和移动对象连接的对象的连接位置
    };

    bool HasHostToMove(IElement* pElement, const std::vector<ElementId>& moveElementIds)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");
        ElementId hostId = pElement->GetHostElementId();
        if (!hostId.IsValid())
            return false;

        FOR_EACH(moveElementId, moveElementIds)
        {
            if (moveElementId == hostId)
            {
                return true;
            }
        }
        return false;
    }

    bool GetActualMoveVector(const IElementPositionPoints* pPositionPoints, const Vector3d& moveVector, Vector3d& actualMoveVector)
    {
        Vector3d elementVector = pPositionPoints->GetControlPoint(0) - pPositionPoints->GetControlPoint(1);
        elementVector.Normalize();
        Vector3d normalVector = elementVector.Cross(moveVector);
        normalVector.Normalize();
        Vector3d actualMoveDir = normalVector.Cross(elementVector);
        double length = actualMoveDir.Dot(moveVector);
        if (MathUtils::IsZero(length))
        {
            return false;
        }

        actualMoveVector = actualMoveDir * length;
        return true;
    }

    OwnerPtr<ICurve3d> GetPositionCurve(IElement* pElem)
    {
        const IElementPositionPoints* pPositionPoints = pElem->GetPositionPoints();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPositionPoints, L"没有定位点?",L"GDMPLab",L"2024-03-30");

        if (pPositionPoints->GetControlPointCount() == 2)
        {
            return ILine3d::Create(pPositionPoints->GetControlPoint(0), pPositionPoints->GetControlPoint(1));
        }
        else if (pPositionPoints->GetControlPointCount() == 3)
        {
            return IArc3d::Create(pPositionPoints->GetControlPoint(0), pPositionPoints->GetControlPoint(1), pPositionPoints->GetControlPoint(2));

        }

        return nullptr;
    }

    OwnerPtr<ICurve3d> GetExtendCurve(ICurve3d* pCurve, JoinPosition joinPos)
    {
        double len = Constants::MAX_MODEL_SPACE_SIZE;
        OwnerPtr<ICurve3d> opExtendCurve = TransferOwnershipCast<ICurve3d>(pCurve->Clone());
        if (joinPos == JoinPosition::Start)
        {
            Intervald limits = opExtendCurve->GetLimits();
            limits.Set(-len, limits.GetMax());
            opExtendCurve->SetLimits(limits);
        }
        else if (joinPos == JoinPosition::End)
        {
            Intervald limits = opExtendCurve->GetLimits();
            limits.Set(limits.GetMin(), len);
            opExtendCurve->SetLimits(limits);
        }

        return opExtendCurve;
    }

    bool CanMoveWithJoining(IElement* pElem, const Vector3d& moveVector, const std::vector<MoveElementJoinData>& joinDatas, std::wstring& strError)
    {
        IDocument* pDoc = pElem->GetDocument();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"没有文档?",L"GDMPLab",L"2024-03-30");

        const IGeometryRelationshipComponent* pGeometryRepCpt = pElem->GetGeometryRelationshipComponent();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pGeometryRepCpt, L"没有连接?",L"GDMPLab",L"2024-03-30");
        const IJoinRelationshipBehavior* pJoinBehavior = pGeometryRepCpt->GetJoinRelationship();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pJoinBehavior, L"没有连接?",L"GDMPLab",L"2024-03-30");

        FOR_EACH(joinData, joinDatas)
        {
            OwnerPtr<ICurve3d> opElementCurve = GetPositionCurve(pElem);
            OwnerPtr<ICurve3d> opExtendElementCurve = GetExtendCurve(opElementCurve.get(), joinData.movedPosition);
            opElementCurve->Translate(moveVector);
            opExtendElementCurve->Translate(moveVector);

            IElement* pOtherElem = pDoc->GetElement(joinData.joinedElementId);
            OwnerPtr<ICurve3d> opOtherElementCurve = GetPositionCurve(pOtherElem);
            OwnerPtr<ICurve3d> opExtendOtherElementCurve = GetExtendCurve(opOtherElementCurve.get(), joinData.joinedPosition);

            std::vector<OwnerPtr<ICurve3dCurve3dIntersection>> opIntersects;
            if (AlgorithmIntersect::Intersect(opExtendElementCurve.get(), opExtendOtherElementCurve.get(), opIntersects) == false)
            {
                strError = GBMP_TR(L"存在端部连接约束，移动超出范围。");
                return false;
            }
        }

        return true;
    }

    bool ExtendCurveToPoint(ICurve3d* pCurve, const Vector3d& point, JoinPosition joinPos)
    {
        double pointParameter = 0.0;
        bool isParameterInRange = false;
        pCurve->GetParameter(point, pointParameter, isParameterInRange, Constants::LENGTH_EPS);
        {
            Intervald limits = pCurve->GetLimits();
            if (limits.GetMin() > pointParameter)
            {
                limits.Set(pointParameter, limits.GetMax());
            }
            if (limits.GetMax() < pointParameter)
            {
                limits.Set(limits.GetMin(), pointParameter);
            }
            else
            {
                if (joinPos == JoinPosition::Start)
                {
                    limits.Set(pointParameter, limits.GetMax());
                }
                else if (joinPos == JoinPosition::End)
                {
                    limits.Set(limits.GetMin(), pointParameter);
                }
            }
            pCurve->SetLimits(limits);
        }

        return true;
    }


    bool UpdateElementPosition(IElement* pElem, ICurve3d* pCurve)
    {
        IElementPositionPoints* pPositionPoints = pElem->GetPositionPoints();
        // 目前只处理直线的情况
        if (pPositionPoints && pPositionPoints->GetControlPointCount() == 2)
        {
            pPositionPoints->SetControlPoint(0, pCurve->GetStartPoint());
            pPositionPoints->SetControlPoint(1, pCurve->GetEndPoint());
        }

        return true;
    }

    bool TrimElement(IDocument* pDoc, const MoveElementJoinData& joinData)
    {
        IElement* pElem = pDoc->GetElement(joinData.movedElementId);
        IElement* pOtherElem = pDoc->GetElement(joinData.joinedElementId);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pElem && pOtherElem, L"输入参数为空?",L"GDMPLab",L"2024-03-30");

        OwnerPtr<ICurve3d> opElementCurve = GetPositionCurve(pElem);
        OwnerPtr<ICurve3d> opExtendElementCurve = GetExtendCurve(opElementCurve.get(), joinData.movedPosition);
        OwnerPtr<ICurve3d> opOtherElementCurve = GetPositionCurve(pOtherElem);
        OwnerPtr<ICurve3d> opExtendOtherElementCurve = GetExtendCurve(opOtherElementCurve.get(), joinData.joinedPosition);

        std::vector<OwnerPtr<ICurve3dCurve3dIntersection>> opIntersects;
        if (AlgorithmIntersect::Intersect(opExtendElementCurve.get(), opExtendOtherElementCurve.get(), opIntersects))
        {
            if (opIntersects.size() > 0)
            {
                ExtendCurveToPoint(opElementCurve.get(), opIntersects[0]->GetIntersectPoint(), joinData.movedPosition);
                ExtendCurveToPoint(opOtherElementCurve.get(), opIntersects[0]->GetIntersectPoint(), joinData.joinedPosition);

                UpdateElementPosition(pElem, opElementCurve.get());
                pDoc->GetRegenerator()->RegenerateElement(pElem->GetElementId());    //必须更新一下，因为如果Element上有约束的话，后面的解算还是按原来的位置解算的
                pElem->FinishEditing();
                UpdateElementPosition(pOtherElem, opOtherElementCurve.get());
                pDoc->GetRegenerator()->RegenerateElement(pOtherElem->GetElementId());    //必须更新一下，因为如果Element上有约束的话，后面的解算还是按原来的位置解算的
                pOtherElem->FinishEditing();
            }
        }

        return true;
    }

    std::vector<MoveElementJoinData> GetJoinDatas(IElement* pElem)
    {
        std::vector<MoveElementJoinData> joinDatas;

        IDocument* pDoc = pElem->GetDocument();
        DBG_WARN_AND_RETURN_UNLESS(pDoc, joinDatas, L"没有文档?",L"GDMPLab",L"2024-03-30");

        auto GetJoinedData = [](IElement* pElem, IElementJoin* pJoin, JoinPosition joinPos) -> MoveElementJoinData {
            if (pElem->GetElementId() == pJoin->GetAttachedId())
            {
                return MoveElementJoinData(pElem->GetElementId(), joinPos, pJoin->GetAttachingId(), pJoin->GetAttachingPosition());
            }
            else
            {
                return MoveElementJoinData(pElem->GetElementId(), joinPos, pJoin->GetAttachedId(), pJoin->GetAttachedPosition());
            }
        };

        const IGeometryRelationshipComponent* pGeoRelationshipComponent = pElem->GetGeometryRelationshipComponent();
        if (!pGeoRelationshipComponent)
            return joinDatas;
        const IJoinRelationshipBehavior* pJoinRelationship = pGeoRelationshipComponent->GetJoinRelationship();
        if (!pJoinRelationship)
            return joinDatas;

        const std::vector<ElementId> joinIdsAtStart = pJoinRelationship->GetElementJoinIds(JoinPosition::Start);
        for (ElementId id : joinIdsAtStart)
        {
            IElementJoin* pJoin = quick_cast<IElementJoin>(pDoc->GetElement(id));
            if (pJoin)
            {
                joinDatas.push_back(GetJoinedData(pElem, pJoin, JoinPosition::Start));
            }
        }

        const std::vector<ElementId> joinIdsAtEnd = pJoinRelationship->GetElementJoinIds(JoinPosition::End);
        for (ElementId id : joinIdsAtEnd)
        {
            IElementJoin* pJoin = quick_cast<IElementJoin>(pDoc->GetElement(id));
            if (pJoin)
            {
                joinDatas.push_back(GetJoinedData(pElem, pJoin, JoinPosition::End));
            }
        }

        const std::vector<ElementId> joinIdsAtMiddle = pJoinRelationship->GetElementJoinIds(JoinPosition::Middle);
        for (ElementId id : joinIdsAtMiddle)
        {
            IElementJoin* pJoin = quick_cast<IElementJoin>(pDoc->GetElement(id));
            if (pJoin)
            {
                joinDatas.push_back(GetJoinedData(pElem, pJoin, JoinPosition::Middle));
            }
        }

        return joinDatas;
    }

    void MoveElementsImpl(IDocument* pDoc, const std::vector<ElementId>& moveElementIds, const Vector3d& originPosition, const Vector3d& moveVector, bool buildJoin, const IModelView* pModelView)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
        OwnerPtr<IElementMoveContext> moveContext = IElementMoveContext::CreateElementMoveContext(pDoc, moveElementIds, moveVector);
        moveContext->SetOriginPosition(originPosition);
        FOR_EACH(moveElementId, moveElementIds)
        {
            IElement* pElement = pDoc->GetElement(moveElementId);
            DBG_WARN_AND_CONTINUE_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");

            // 如果自身和宿主对象都在移动集合里，则仅移动宿主对象即可，从属对象由PositionHelper定位跟随。
            if (HasHostToMove(pElement, moveElementIds))
                continue;

            ElementJoinUtils::DeleteAllElementJoins(pDoc, moveElementId);

            // 构件移动行为（支持定制）
            ElementTransformationHandler* pElementTransformationHandler = ElementTransformationHandlerManager::GetInstance()->Get(pElement);
            if (pElementTransformationHandler)
            {
                ElementTransformationContext transformContext(*moveContext);
                transformContext.ModelView = pModelView;
                pElementTransformationHandler->Translate(pElement, transformContext);
            }

            // 必须更新一下，否则如果Element上有约束的话，后面的解算还是按原来的位置解算的
            pElement->GetDocument()->GetRegenerator()->RegenerateElement(moveElementId);
            pElement->FinishEditing();

            if (buildJoin)
            {
                const IModifyJoinRelationship *pModifyJoinRelationship = IModifyJoinRelationship::GetModifyJoinRelationship();
                if (pModifyJoinRelationship)
                {
                    pModifyJoinRelationship->BuildJoinInMoveElements(pDoc, moveElementId);
                }
            }
        }
    }
}

void gcmp::GbmpMoveUtils::MoveElements(IDocument* pDoc, const std::vector<ElementId>& moveElementIds, const Vector3d& originPosition, const Vector3d& moveVector, const IModelView* pModelView)
{
    MoveElementsImpl(pDoc, moveElementIds, originPosition, moveVector, true, pModelView);
}

void gcmp::GbmpMoveUtils::MoveElementsForcely(IDocument* pDoc, const std::vector<ElementId>& moveElementIds, const Vector3d& moveVector, const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    OwnerPtr<IElementMoveContext> moveContext = IElementMoveContext::CreateElementMoveContext(pDoc, moveElementIds, moveVector);
    FOR_EACH(moveElementId, moveElementIds)
    {
        IElement* pElement = pDoc->GetElement(moveElementId);
        DBG_WARN_AND_CONTINUE_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");
        if (HasHostToMove(pElement, moveElementIds))
        {
            continue;
        }

        ElementTransformationHandler* pElementTransformationHandler = ElementTransformationHandlerManager::GetInstance()->Get(pElement);
        if (pElementTransformationHandler)
        {
            ElementTransformationContext transformContext(*moveContext);
            transformContext.ModelView = pModelView;
            pElementTransformationHandler->Translate(pElement, transformContext);
        }
    }
}

void gcmp::GbmpMoveUtils::MoveElement(IElement* pElement, const Vector3d& originPosition, const Vector3d& moveVector, const IModelView* pModelView)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");

    std::vector<ElementId> elementIds;
    elementIds.push_back(pElement->GetBasicInformation()->GetElementId());
    return GbmpMoveUtils::MoveElements(pElement->GetDocument(), elementIds, originPosition, moveVector, pModelView);
}

bool gcmp::GbmpMoveUtils::MoveElementWithJoining(IElement* pElement, const Vector3d& originPosition, const Vector3d& moveVector, const IModelView* pModelView, std::wstring& strError)
{
    std::vector<MoveElementJoinData> joinDatas = GetJoinDatas(pElement);
    if (joinDatas.size() > 0)
    {
        // 计算有墙连接的真实移动方向：墙体只可沿着垂直于自身定位线的方向移动
        const IElementPositionPoints* pPositionPoints = pElement->GetPositionPoints();
        // 目前只处理直线的情况
        if (pPositionPoints && pPositionPoints->GetControlPointCount() == 2)
        {
            Vector3d actualMoveVector = moveVector;
            if (GetActualMoveVector(pPositionPoints, moveVector, actualMoveVector) == false)
            {
                return false;
            }

            if (CanMoveWithJoining(pElement, actualMoveVector, joinDatas, strError) == false)
            {
                return false;
            }

            MoveElementsImpl(pElement->GetDocument(), { pElement->GetElementId() }, originPosition, actualMoveVector, false, pModelView);

            FOR_EACH(iter, joinDatas)
            {
                if (TrimElement(pElement->GetDocument(), iter) == false)
                {
                    return false;
                }
            }

            // 再做一遍连接和扣减
            const IModifyJoinRelationship *pModifyJoinRelationship = IModifyJoinRelationship::GetModifyJoinRelationship();
            if (pModifyJoinRelationship)
            {
                pModifyJoinRelationship->BuildJoinInMoveElements(pElement->GetDocument(), pElement->GetElementId());
            }

            return true;
        }
    }

    GbmpMoveUtils::MoveElement(pElement, originPosition, moveVector, pModelView);
    return true;
}
